package exp_handle_pkg;

  import uvm_pkg::*;
  `include "uvm_macros.svh"

  //sequence item
  class exp_trans extends uvm_sequence_item;
    rand bit [11-1:0] exp1          ;
    rand bit [11-1:0] exp2          ;
    bit               code          ;
    bit               signal_double ;
    bit               rsp           ;

    constraint cexp{
      //约束指数在规格化数之内
      soft exp1 > 0 ;
      soft exp1 < 11'b111_1111_1111;  
      soft exp2 > 0 ;
      soft exp2 < 11'b111_1111_1111; 
    };

    `uvm_object_utils_begin(exp_trans)
      `uvm_field_int(exp1, UVM_ALL_ON)
      `uvm_field_int(exp2, UVM_ALL_ON)
      `uvm_field_int(code, UVM_ALL_ON)
      `uvm_field_int(signal_double, UVM_ALL_ON)   
    `uvm_object_utils_end

    function new(string name = "exp_trans");
      super.new(name);
    endfunction    
      
  endclass: exp_trans

  //driver
  class exp_driver extends uvm_driver #(exp_trans);
    local virtual exp_intf intf;

    `uvm_component_utils(exp_driver)
    
    function new(string name = "exp_driver", uvm_component parent);
      super.new(name, parent);
    endfunction

    function void set_interface(virtual exp_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction

    task run_phase(uvm_phase phase);
      fork
        this.do_drive();
      join
    endtask

    task do_drive();
      exp_trans req, rsp;
      @(posedge intf.rstn);
      forever begin
        seq_item_port.get_next_item(req);
        this.exp_write(req);
        void'($cast(rsp, req.clone()));
        rsp.rsp = 1;
        rsp.set_sequence_id(req.get_sequence_id());
        seq_item_port.item_done(rsp);
      end
    endtask

    task exp_write(input exp_trans t);
      intf.code <= t.code;
      intf.signal_double <= t.signal_double;
      intf.exp1 <= t.exp1;
      intf.exp2 <= t.exp2;
    endtask

  endclass

  class exp_sequencer extends uvm_sequencer #(exp_trans);
    `uvm_component_utils(exp_sequencer)
    function new(string name = "exp_sequencer", uvm_component parent);
      super.new(name, parent);
    endfunction
  endclass

  class exp_data_sequence extends uvm_sequence #(exp_trans);
    rand bit [11-1:0] exp1 = -1          ;
    rand bit [11-1:0] exp2 = -1          ;
    rand int ntrans = 100                ;

  constraint cstr{
    soft exp1 == -1;
    soft exp2 == -2;
    soft ntrans == 100;
  }

  `uvm_object_utils_begin(exp_data_sequence)
    `uvm_field_int(exp1, UVM_ALL_ON)
    `uvm_field_int(exp2, UVM_ALL_ON)
  `uvm_object_utils_end
  `uvm_declare_p_sequencer(exp_sequencer)

  function new (string name = "exp_data_sequence");
    super.new(name);
  endfunction

  task body();
    repeat(ntrans) send_trans();
  endtask

  task send_trans();
    exp_trans req, rsp;
    `uvm_do_with(req, {
      local::exp1 >= 0 -> exp1 == local::exp1;
      local::exp2 >= 0 -> exp2 == local::exp2;
    })
    `uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
    get_response(rsp);
    `uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
    assert(rsp.rsp)
      else $error("[exp send error] %0t exp can't send!", $time);
  endtask

  function void post_randomize();
      string s;
      s = {s, "AFTER RANDOMIZATION \n"};
      s = {s, "=======================================\n"};
      s = {s, "exp_data_sequence object content is as below: \n"};
      s = {s, super.sprint()};
      s = {s, "=======================================\n"};
      `uvm_info(get_type_name(), s, UVM_HIGH)
    endfunction

  endclass
    
  typedef struct packed {
    bit [11-1:0] exp1;
    bit [11-1:0] exp2;
    bit          signal_double;
    bit          code;
  } exp_data_t;

  // monitor
  class exp_monitor extends uvm_monitor;
    local virtual exp_intf intf;
    uvm_blocking_put_port #(exp_data_t) mon_bp_port;

    `uvm_component_utils(exp_monitor)

    function new (string name="exp_monitor", uvm_component parent);
      super.new(name, parent);
      mon_bp_port = new("mon_bp_port", this);
    endfunction

    function void set_interface(virtual chnl_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction

    task run_phase(uvm_phase phase);
      this.mon_trans();
    endtask

    task mon_trans();
      exp_data_t m;
      forever begin
        @(posedge intf.clk);
        m.exp1 = intf.mon_ck.exp1;
        m.exp2 = intf.mon_ck.exp2;
        m.signal_double = intf.mon_ck.signal_double;
        m.code = intf.mon_ck.code;
        mon_bp_port.put(m);
        `uvm_info(get_type_name(), $sformatf("monitored channel data 'h%8x 'h%8x", m.exp1, m.exp2), UVM_HIGH)
      end
    endtask
  endclass

  //agent
  class exp_agent extends uvm_agent;
    exp_driver driver;
    exp_monitor monitor;
    exp_sequencer sequencer;
    local virtual exp_intf vif;

    `uvm_component_utils(exp_agent);

    function new(string name = "exp_agent", uvm_component parent);
      super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      driver = exp_driver::type_id::create("driver", this);
      monitor = exp_monitor::type_id::create("monitor", this);
      sequencer = exp_sequencer::type_id::create("sequencer", this);
    endfunction

    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      driver.seq_item_port.connect(sequencer.seq_item_export);
    endfunction

    function void set_interface(virtual exp_intf vif);
      this.vif = vif;
      driver.set_interface(vif);
      monitor.set_interface(vif);
    endfunction

  endclass

endpackage
